package com.justgame.project.generator.schema;

import com.justgame.project.cloud.base.entity.BaseEntity;
import com.justgame.project.cloud.base.entity.BaseTable;
import com.justgame.project.generator.CodeGeneratorApplication;
import com.justgame.project.generator.container.EntityContainer;
import com.justgame.project.generator.mapper.CreateTableMapper;
import com.justgame.project.generator.properties.GlobalProperties;
import com.justgame.project.generator.transform.SQLCreatorTransform;
import com.justgame.project.generator.utils.ClassUtils;
import com.justgame.project.generator.utils.NameUtils;
import com.justgame.project.generator.verify.IEntityValidation;
import lombok.extern.log4j.Log4j2;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Log4j2
@Component
public class TableBuilder {
    private final GlobalProperties globalProperties;
    private final List<IEntityValidation> validations;
    private final SQLCreatorTransform sqlCreatorTransform;
    private final CreateTableMapper createTableMapper;
    private final EntityContainer entityContainer;
    private final List<TableSQLCreator> creators = Collections.synchronizedList(new ArrayList<>());

    public TableBuilder(GlobalProperties globalProperties, List<IEntityValidation> validations, SQLCreatorTransform sqlCreatorTransform, CreateTableMapper createTableMapper, EntityContainer entityContainer) {
        this.globalProperties = globalProperties;
        this.validations = validations.stream()
                .filter((valid)->Boolean.TRUE.equals(valid.enable()))
                .sorted(Comparator.comparing(IEntityValidation::sort))
                .collect(Collectors.toList());
        this.sqlCreatorTransform = sqlCreatorTransform;
        this.createTableMapper = createTableMapper;
        this.entityContainer = entityContainer;
    }

    @Transactional
    public synchronized void build(){
        List<Entity> entities = entityContainer.getEntities();
        if (Objects.isNull(entities) || entities.isEmpty()){
            return;
        }
        createTableMapper.dropTables(entityContainer.entityTableNames());
        entities.forEach(entity -> {
            try{
                Assert.notNull(entity.getEntityType(),"实体： "+entity.getName()+" 未设置实体类型");
                switch (entity.getEntityType()){
                    case BASE_ENTITY:
                        entity.setSuperClass(BaseEntity.class);
                        break;
                    case BASE_TABLE:
                        entity.setSuperClass(BaseTable.class);
                        break;
                    default:
                        throw new RuntimeException("支持此实体类型");
                }
                validations.forEach(validation -> validation.valid(entity));
                baseServiceHandle(entity);
                entity.getServiceItems().forEach(item -> handleEach(entity,item));
                /* 处理实体类中的表关联 */
                List<Join> joinTables = entity.getJoinTableBuilders().stream()
                        .map((joinBuilder)-> joinBuilder.build(entity,entityContainer))
                        .collect(Collectors.toList());
                Assert.isTrue(joinTables.size() == joinTables.stream().map(Join::getAlias).collect(Collectors.toSet()).size(),entity.getName() + "表中存在重复的表关联别名");
                entity.setJoinTables(joinTables);
                TableSQLCreator creator = sqlCreatorTransform.convert(entity);
                createTableMapper.createTable(creator);
                creators.add(creator);
            }catch (IllegalArgumentException exc){
                log.error("构建 "+entity.getName()+" 实体类时发生异常："+exc.getMessage());
                TransactionAspectSupport.currentTransactionStatus();
                System.exit(1);
            }
        });
    }

    public List<TableSQLCreator> getCreators() {
        return creators;
    }

    public void handleEach(Entity entity, Service.ServiceEach each){
        StringBuilder sb = new StringBuilder(each.getResultType().prefix);
        Iterator<Service.By> iterator = each.getItems().iterator();
        while (iterator.hasNext()){
            Service.By by = iterator.next();
            Optional<Field> fieldOptional = entity.getFields().stream().filter(f->f.getName().equals(by.getFieldName())).findFirst();
            Assert.isTrue(fieldOptional.isPresent(),"实体类 "+entity.getName()+" 在构建Service方法时，未找到匹配的字段 "+by.getFieldName());
            sb.append(NameUtils.firstWorldUpper(by.getFieldName()));
            if (!each.getResultType().equals(Service.ServiceType.UPDATE)){
                Optional.ofNullable(by.getFilterType()).ifPresent(
                        filterType -> sb.append(NameUtils.firstWorldUpper(filterType.getServiceMethodName()))
                );
            }
            if (iterator.hasNext()){
                sb.append("And");
            }
            by.setEntity(entity);
            by.setField(fieldOptional.get());
        }
        each.setDescription(handleServiceDescription(each));
        each.setMethodName(StringUtils.hasLength(each.getMethodName()) ? each.getMethodName() : sb.toString());
    }

    public String handleServiceDescription(Service.ServiceEach each){
        if (StringUtils.hasLength(each.getDescription())){
            return each.getDescription();
        }
        switch (each.getResultType()){
            case UPDATE:
                return "更新相关字段：" + each.getItems().stream().map(by->by.getField().getComment()).collect(Collectors.joining("、"));
            case COUNT:
            case QUERY_LIST:
            case QUERY_ONE:
                return "查询通过字段：" + each.getItems().stream().map(by->by.getField().getComment()).collect(Collectors.joining("、"));
            case ADD:
                return "新增通过字段：" + each.getItems().stream().map(by->by.getField().getComment()).collect(Collectors.joining("、"));
        }
        return "";
    }

    public void baseServiceHandle(Entity entity){
        entity.getServiceItems().addAll(
                Service.builder()
                        .addBy(
                                Service.AddBy.builder()
                                        .name("baseAdd")
                                        .description("新增创建")
                                        .params(entity.getFields().stream()
                                                .filter(field->!ClassUtils.classFieldNameList(entity.getSuperClass()).contains(field.getName()))
                                                .map(field -> Service.AddBy.Param.column(field.getName()))
                                                .toArray(Service.AddBy.Param[]::new))
                                        .build()
                        )
                        .updateBy(
                                Service.UpdateBy.builder()
                                        .name("baseUpdate")
                                        .description("更新")
                                        .params(
                                                entity.getFields().stream()
                                                        .map(Field::getName)
                                                        .filter(name ->!ClassUtils.classFieldNameList(entity.getSuperClass()).contains(name))
                                                        .toArray(String[]::new)
                                        )
                                        .build()
                        )
                        .build()
        );
    }
}
